package howl.model;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import compiler.HowlToken;
import compiler.exceptions.HowlModelException;
import compiler.exceptions.HowlUnauthorisedAction;
import compiler.nonterminals.*;
import compiler.terminals.DataDecl;
import compiler.terminals.DataValue;
import howl.types.*;

public class Handler {

	private Model m;

	private HowlUser hu;

	private ServiceHandler service;

	public Handler(HowlUser hu) {
		super();
		this.m = new Model(hu);
		this.service = new ServiceHandler(hu);
		this.hu = hu;
	}

	public void Handle(List<HowlToken> hts) throws HowlUnauthorisedAction,
			HowlModelException, IOException {
		for (HowlToken ht : hts)
			this.Handle(ht);
	}

	public void Handle(HowlToken ht) throws HowlUnauthorisedAction,
			HowlModelException, IOException {

		if (ht instanceof AlarmStmt) {
			this.doAlarm((AlarmStmt) ht);
		} else if (ht instanceof CreateStmt) {
			this.doCreate((CreateStmt) ht);
			this.service.handle(ht);

		} else if (ht instanceof DeleteStmt) {
			this.service.handle(ht);
			this.doDelete((DeleteStmt) ht);

		} else if (ht instanceof DeregisterStmt) {
			this.doDeregister((DeregisterStmt) ht);

		} else if (ht instanceof InsertStmt) {
			this.doInsert((InsertStmt) ht);
			this.service.handle(ht);

		} else if (ht instanceof RegisterStmt) {
			this.doRegister((RegisterStmt) ht);

		} else if (ht instanceof ReportStmt) {
			this.doReport((ReportStmt) ht);

		}

		// TODO ERROR HANDLING FOR UNHANDLED HowlToken's
	}

	private void doAlarm(AlarmStmt a) throws HowlUnauthorisedAction,
			HowlModelException, IOException {
		Alarm alarm = new Alarm(a.getExp().getExp());
		alarm.setFeedname(a.getFeedname().getFeedname());
		m.addAlarm(alarm);
	}

	private void doCreate(CreateStmt c) throws HowlModelException {
		TableDescriptor td = new TableDescriptor(c.getFeedname().getFeedname());
		td.setOwner(this.hu.getUsers());
		ArrayList<String> names = new ArrayList<String>();
		ArrayList<Unit> units = new ArrayList<Unit>();

		List<DataDecl> decs = c.getDeclarations();
		for (int i = 0; i < decs.size(); i++) {
			DataDecl d = decs.get(i);
			names.add(d.getName());
			units.add(d.getDimension());
		}
		td.setUnitmapper(units);
		td.setStringmapper(names);
		this.m.addTableDescriptor(td);
	}

	private void doDelete(DeleteStmt d) throws HowlModelException {
		this.m.deleteTableDescriptor(this.m.getTableDescriptor(d.getFeedname()
				.getFeedname()));
	}

	private void doDeregister(DeregisterStmt d) throws HowlModelException {
		this.m.removeDataProvider(this.m.getTableDescriptor(d.getFeedname()
				.getFeedname()), d.getDataprovider().getName());

	}

	private void doInsert(InsertStmt i) throws HowlModelException {
		TableDescriptor td = this.m.getTableDescriptor(i.getFeedname()
				.getFeedname());
		if (td != null) {
			DataProvider dps = this.m.getDataProvider(td, i.getDataprovider()
					.getName());
			if (this.m.hasAccess(td) && (dps != null)) {
				List<DataValue> vals = i.getDatavalues();

				Entry e = new Entry(td, i.getDataprovider().getName());
				this.m.registerObject(e);

				Datum d;

				for (int j = 0; j < vals.size(); j++) {
					DataValue dv = vals.get(j);
					d = new Datum(td, e, dv.getName(), dv.getValue()
							.doubleValue());
					this.m.registerObject(d);
				}

			}
		} else {
			// TODO ERROR
		}

	}

	private void doRegister(RegisterStmt r) throws HowlModelException {
		DataProvider dt = new DataProvider();
		TableDescriptor td = this.m.getTableDescriptor(r.getFeedname()
				.getFeedname());
		dt.setName(r.getDataProvider().getName());
		dt.setLatitude(r.getGeolocation().getLatitude().doubleValue());
		dt.setLongitude(r.getGeolocation().getLongtitude().doubleValue());
		dt.setFeedOwner(td.getId());
		this.m.addDataProvider(td, dt);
	}

	private void doReport(ReportStmt r) throws IOException {
		
		if (r.getTop() != null && r.getFreq() == null) {
			// Generate Instance report
		} else {
			Report rep = new Report(r.getExp().getExp());
			rep.setFeedName(r.getFeedname().getFeedname());
			rep.setWhen(r.getFreq().getWhen());
			rep.setTimeunit(r.getFreq().getTimeunit());
			rep.setRate(r.getFreq().getRate());
			rep.setOften(r.getFreq().getOften());
			this.m.addReport(rep);
		}

	}

	/**
	 * @return the m
	 */
	public Model getModel() {
		return m;
	}

}
